package com.hermes.biz.common.util.security;

import java.io.ByteArrayOutputStream;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang.ArrayUtils;
import javax.crypto.Cipher;

public class RSAUtils {
	public static final String KEY_ALGORITHM = "RSA";
    
	     
	    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";

	     
	    private static final String PUBLIC_KEY = "RSAPublicKey";
	    
	     
	    private static final String PRIVATE_KEY = "RSAPrivateKey";
	    
	     
	    private static final int MAX_ENCRYPT_BLOCK = 117;
	    
	     
	    private static final int MAX_DECRYPT_BLOCK = 128;

	     
	     public static Map genKeyPair() throws Exception {
	        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
	        long startTime=System.nanoTime(); 
	        //初始化
	        keyPairGen.initialize(1024);
	        //生成keyPair
	        KeyPair keyPair = keyPairGen.generateKeyPair();
	        long endTime=System.nanoTime(); 
	        System.out.println("耗时： "+(endTime-startTime)/1000000+"毫秒");
	        //ȡpk
	        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
	        //ȡsk
	        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
	        //存储key pair
	        Map keyMap = new HashMap(2);
	        keyMap.put(PUBLIC_KEY, publicKey);
	        keyMap.put(PRIVATE_KEY, privateKey);
	        return keyMap;
	    }
	    
	     
	     public static String sign(byte[] data, String privateKey) throws Exception {
	        byte[] keyBytes = Base64Utils.decode(privateKey);
	        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	        PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
	        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
	        signature.initSign(privateK);
	        signature.update(data);
	        return Base64Utils.encode(signature.sign());
	    }

	     
	    public static boolean verify(byte[] data, String publicKey, String sign)
	             throws Exception {
	        byte[] keyBytes = Base64Utils.decode(publicKey);
	        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	        PublicKey publicK = keyFactory.generatePublic(keySpec);
	        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
	        signature.initVerify(publicK);
	        signature.update(data);
	        return signature.verify(Base64Utils.decode(sign));
	    }

	     
	    public static byte[] decryptByPrivateKey(byte[] encryptedData, String privateKey)
	             throws Exception {
	        byte[] keyBytes = Base64Utils.decode(privateKey);
	        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
	        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
	        cipher.init(Cipher.DECRYPT_MODE, privateK);
	        int inputLen = encryptedData.length;
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        int offSet = 0;
	        byte[] cache;
	        int i = 0;
	        System.out.println("=====================================");
	        System.out.println("解密encryptedDate length:"+inputLen);
	        // 分段解密
	         while (inputLen - offSet > 0) {
	             if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
	                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
	             } else {
	                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
	            }
	            out.write(cache, 0, cache.length);
	            i++;
	            offSet = i * MAX_DECRYPT_BLOCK;
	        }
	        byte[] decryptedData = out.toByteArray();
	        out.close();
	        return decryptedData;
	    }

	     
	    public static byte[] decryptByPublicKey(byte[] encryptedData, String publicKey)
	             throws Exception {
	        byte[] keyBytes = Base64Utils.decode(publicKey);
	        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	        Key publicK = keyFactory.generatePublic(x509KeySpec);
	        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
	        cipher.init(Cipher.DECRYPT_MODE, publicK);
	        int inputLen = encryptedData.length;
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        int offSet = 0;
	        byte[] cache;
	        int i = 0;
	        // 
	        System.out.println("=====================================");
	        System.out.println("加密encryptedDate length:"+inputLen);
	         while (inputLen - offSet > 0) {
	             if (inputLen - offSet > MAX_DECRYPT_BLOCK) {
	                cache = cipher.doFinal(encryptedData, offSet, MAX_DECRYPT_BLOCK);
	             } else {
	                cache = cipher.doFinal(encryptedData, offSet, inputLen - offSet);
	            }
	            out.write(cache, 0, cache.length);
	            i++;
	            offSet = i * MAX_DECRYPT_BLOCK;
	        }
	        byte[] decryptedData = out.toByteArray();
	        out.close();
	        return decryptedData;
	    }

	    // RSA Public key 加密
	    public static byte[] encryptByPublicKey(byte[] data, String publicKey)
	             throws Exception {
	        byte[] keyBytes = Base64Utils.decode(publicKey);
	        byte[] datebytes = new String(data).getBytes();
	        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	        Key publicK = keyFactory.generatePublic(x509KeySpec);
	        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
	        cipher.init(Cipher.ENCRYPT_MODE, publicK);
	        int inputLen = data.length;
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        int offSet = 0;
	        byte[] cache;
	        int i = 0;
	        byte[] encodedBytes = new byte[]{};
//	        for(int j = 0;j<datebytes.length;j+=102){
//	        	byte[] subarray = ArrayUtils.subarray(datebytes, i, i+102);
//	        	byte[] doFinal = cipher.doFinal(subarray);
//	        	encodedBytes = ArrayUtils.addAll(encodedBytes, doFinal);
//	        }
	        
//	        //分段加密
	         while (inputLen - offSet > 0) {
	             if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
	                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
	             } else {
	                cache = cipher.doFinal(data, offSet, inputLen - offSet);
	            }
	            out.write(cache, 0, cache.length);
	            i++;
	            offSet = i * MAX_ENCRYPT_BLOCK;
	        }
	        byte[] encryptedData = out.toByteArray();
	        out.close();
	        //return encryptedData;
	        return encryptedData;
	    }

	     
	    public static byte[] encryptByPrivateKey(byte[] data, String privateKey)
	             throws Exception {
	        byte[] keyBytes = Base64Utils.decode(privateKey);
	        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
	        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
	        Key privateK = keyFactory.generatePrivate(pkcs8KeySpec);
	        Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
	        cipher.init(Cipher.ENCRYPT_MODE, privateK);
	        int inputLen = data.length;
	        ByteArrayOutputStream out = new ByteArrayOutputStream();
	        int offSet = 0;
	        byte[] cache;
	        int i = 0;
	         while (inputLen - offSet > 0) {
	             if (inputLen - offSet > MAX_ENCRYPT_BLOCK) {
	                cache = cipher.doFinal(data, offSet, MAX_ENCRYPT_BLOCK);
	             } else {
	                cache = cipher.doFinal(data, offSet, inputLen - offSet);
	            }
	            out.write(cache, 0, cache.length);
	            i++;
	            offSet = i * MAX_ENCRYPT_BLOCK;
	        }
	        byte[] encryptedData = out.toByteArray();
	        out.close();
	        return encryptedData;
	    }

	     
	    public static String getPrivateKey(Map keyMap)
	             throws Exception {
	        Key key = (Key) keyMap.get(PRIVATE_KEY);
	        return Base64Utils.encode(key.getEncoded());
	    }

	     
	    public static String getPublicKey(Map keyMap)
	             throws Exception {
	        Key key = (Key) keyMap.get(PUBLIC_KEY);
	        return Base64Utils.encode(key.getEncoded());
	    }
}
